﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Diploma.Models;

namespace DiplomaWeb.TimeLines
{
    public class TimeLineItem
    {
        protected int _marginLeft;
        protected string _startDate;
        protected string _type;

        public TimeLineItem(string startDate, string type, int marginLeft)
        {
            _startDate = startDate;
            _type = type;
            _marginLeft = marginLeft;
        }

        public int MarginLeft
        {
            get { return _marginLeft; }
        }

        public string StartDate
        {
            get { return _startDate; }
        }

        public string Type
        {
            get { return _type; }
        }
    }

    public class TimeLineCheckPoint : TimeLineItem
    {
        private readonly TimeLineConnector _connector;
        private readonly Guid _taskId;

        public TimeLineCheckPoint(string startDate, string type, int marginLeft, Guid taskId,
                                  TimeLineConnector connector) : base(startDate, type, marginLeft)
        {
            _taskId = taskId;
            _connector = connector;
        }

        public Guid TaskId
        {
            get { return _taskId; }
        }

        public TimeLineConnector Connector
        {
            get { return _connector; }
        }
    }

    public class TimeLineConnector
    {
        private readonly string _color;
        private readonly int _width;

        public TimeLineConnector(int width, string color)
        {
            _width = width;
            _color = color;
        }

        public int Width
        {
            get { return _width; }
        }

        public string Color
        {
            get { return _color; }
        }
    }

    public class TimeLineMonthSeparator : TimeLineItem
    {
        public TimeLineMonthSeparator(string startDate, string type, int marginLeft)
            : base(startDate, type, marginLeft)
        {
        }
    }

    public class TimeLine
    {
        private readonly List<CheckPoint> _checkPointsList;
        private int _timeLineMinInterval = 31;
        private int _timeLineStep = 150;
        public int _zoomStep = 20;

        public TimeLine(IEnumerable<CheckPoint> checkPointsList)
        {
            if (checkPointsList == null)
                throw new ArgumentNullException("checkPointsList");

            _checkPointsList = checkPointsList.OrderBy(tl => tl.StartDate).ToList();
        }

        public int ZoomStep
        {
            get { return _zoomStep; }
            private set { _zoomStep = value; }
        }

        public int TimeLineMinInterval
        {
            get { return _timeLineMinInterval; }
            private set { _timeLineMinInterval = value; }
        }

        public int TimeLineStep
        {
            get { return _timeLineStep; }
            set { _timeLineStep = value; }
        }


        public IEnumerable<TimeLineItem> GetTimeline(int? scale, Student student)
        {
            if (_checkPointsList.Count == 0)
                return null;
            if (_checkPointsList.Count == 1)
            {
                CheckPoint cp = _checkPointsList[0];
                return new TimeLineItem[]
                           {
                               new TimeLineCheckPoint
                                   (cp.StartDate.Value.Date.ToShortDateString(),
                                    cp.GetType().Name,
                                    0,
                                    cp.CheckPointId,
                                    null)
                           };
            }

            _timeLineMinInterval = GetMinimalDistance();

            if (scale != null && scale.Value >= 80 && scale.Value <= 300)
                _timeLineStep = scale.Value;
            var list = new List<TimeLineItem>();
            for (int i = 0; i < _checkPointsList.Count - 1; i++)
            {
                CheckPoint currentTask = _checkPointsList[i];
                CheckPoint nextTask = _checkPointsList[i + 1];
                string connectorColor = "Red";

                if ((nextTask is Task) && ((Task) nextTask).IsCompleted)
                {
                    connectorColor = "Green";
                }
                else if (nextTask is GlobalTask && student != null)
                {
                    GlobalTasksCompletion completion =
                        (nextTask as GlobalTask).GlobalTasksCompletions.Where(c => c.StudentId == student.UserId).
                            FirstOrDefault();
                    if (completion != null && completion.IsComplete)
                        connectorColor = "Green";
                }

                var tlCheckPoint = new TimeLineCheckPoint
                    (currentTask.StartDate.Value.ToShortDateString(),
                     currentTask.GetType().Name,
                     GetDistance(_checkPointsList.First().StartDate.Value, currentTask.StartDate.Value),
                     currentTask.CheckPointId,
                     new TimeLineConnector(GetDistance(currentTask.StartDate.Value, nextTask.StartDate.Value),
                                           connectorColor));

                list.Add(tlCheckPoint);
                list.AddRange(GetMonthSeparators(currentTask.StartDate.Value, nextTask.StartDate.Value, null));
            }

            CheckPoint lastPoint = _checkPointsList.Last();
            var tlLastPoint = new TimeLineCheckPoint
                (lastPoint.StartDate.Value.Date.ToShortDateString(),
                 lastPoint.GetType().Name,
                 GetDistance(_checkPointsList.First().StartDate.Value, lastPoint.StartDate.Value),
                 lastPoint.CheckPointId,
                 null);

            list.Add(tlLastPoint);

            if (_checkPointsList.Last().StartDate > DateTime.Now)
            {
                list.Add(new TimeLineItem(
                             DateTime.Now.ToShortDateString(),
                             "CurrentDate",
                             GetDistance(_checkPointsList.First().StartDate.Value, DateTime.Now)));
            }

            return list;
        }


        private int GetMinimalDistance()
        {
            if (_checkPointsList.Count < 2)
                return 0;
            int minDistance = 31;
            for (int i = 0; i < _checkPointsList.Count - 1; i++)
            {
                int distance = (_checkPointsList[i + 1].StartDate.Value - _checkPointsList[i].StartDate.Value).Days;
                if (distance < minDistance)
                {
                    minDistance = distance;
                }
            }

            return minDistance;
        }

        private List<TimeLineItem> GetMonthSeparators(DateTime startDate, DateTime endDate,
                                                      List<TimeLineItem> timeLineItems)
        {
            if (timeLineItems == null)
                timeLineItems = new List<TimeLineItem>();

            DateTime montsSeparator = startDate.AddMonths(1);
            montsSeparator = RoundDate(montsSeparator);
            if (montsSeparator < endDate)
            {
                var tlMonthsSeparator = new TimeLineMonthSeparator
                    (CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(montsSeparator.Month),
                     "MonthsSeparator",
                     GetDistance(_checkPointsList.First().StartDate.Value, montsSeparator));

                timeLineItems.Add(tlMonthsSeparator);
                return GetMonthSeparators(montsSeparator, endDate, timeLineItems);
            }
            return timeLineItems;
        }

        private int GetDistance(TimeSpan interval)
        {
            int days = interval.Days;
            if (days == 0)
                return 0;
            return days*TimeLineStep/TimeLineMinInterval;
        }

        private int GetDistance(DateTime startDate, DateTime endDate)
        {
            //#if DEBUG
            //    if (startDate == endDate)
            //    {
            //        var v = 5;
            //    }
            //#endif
            
            TimeSpan interval = endDate - startDate;
            return GetDistance(interval);
        }

        private DateTime RoundDate(DateTime dateTime)
        {
            if (dateTime.Day == 1)
                return dateTime;
            int year = dateTime.Year;
            int month = dateTime.Month;
            if (month + 1 > 12)
            {
                month = 1;
                year += 1;
            }
            var newDate = new DateTime(year, month, 1);

            return newDate;
        }
    }
}